/*******************************************************************************
 * Software Name : RCS IMS Stack
 *
 * Copyright (C) 2010-2016 Orange.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.gsma.rcs.core.ims.protocol.sdp;

import java.io.ByteArrayInputStream;
import java.util.Vector;

/**
 * SDP parser
 *
 * @author jexa7410
 */
public class SdpParser extends Parser {
    /**
     * Session description
     */
    public SessionDescription sessionDescription = new SessionDescription();

    /**
     * Media description
     */
    public Vector<MediaDescription> mediaDescriptions = new Vector<>();

    /**
     * Input stream
     */
    private ByteArrayInputStream bin = null;

    /**
     * Constructor
     *
     * @param data Data
     */
    public SdpParser(byte data[]) {
        bin = new ByteArrayInputStream(data);
        if (getToken(bin, "v=")) {
            parseSessionDescription();
            parseMediaDescriptions();
        }
    }

    /**
     * Parse session description
     */
    private void parseSessionDescription() {
        // Protocol version
        sessionDescription.version = getLine(bin);

        // Origin
        if (getToken(bin, "o=")) {
            sessionDescription.origin = getLine(bin);
        }

        // Session name
        if (getToken(bin, "s=")) {
            sessionDescription.sessionName = getLine(bin);
        }

        // Session and media Information
        if (getToken(bin, "i=")) {
            sessionDescription.sessionInfo = getLine(bin);
        }

        // URI
        if (getToken(bin, "u=")) {
            sessionDescription.uri = getLine(bin);
        }

        // E-Mail
        if (getToken(bin, "e=")) {
            sessionDescription.email = getLine(bin);
        }

        // Phone number
        if (getToken(bin, "p=")) {
            sessionDescription.phone = getLine(bin);
        }

        // Connection information
        if (getToken(bin, "c=")) {
            sessionDescription.connectionInfo = getLine(bin);
        }

        // Bandwidth information
        while (getToken(bin, "b=")) {
            // session bandwidth information is not used right now; just consumed for parsing
            sessionDescription.bandwidthInfo = getLine(bin);
        }

        // Time description
        sessionDescription.timeDescriptions = new Vector<>();
        while (getToken(bin, "t=")) {
            TimeDescription timeDescription = parseTimeDescription();
            this.sessionDescription.timeDescriptions.addElement(timeDescription);
        }

        // Time zone adjustments
        if (getToken(bin, "z=")) {
            sessionDescription.timezoneAdjustment = getLine(bin);
        }

        // Encryption key
        if (getToken(bin, "k=")) {
            sessionDescription.encryptionKey = getLine(bin);
        }

        // Session attributes
        sessionDescription.sessionAttributes = new Vector<>();
        while (getToken(bin, "a=")) {
            String sessionAttribute = getLine(bin);
            int index = sessionAttribute.indexOf(':');
            if (index > 0) {
                String name = sessionAttribute.substring(0, index);
                String value = sessionAttribute.substring(index + 1);
                MediaAttribute attribute = new MediaAttribute(name, value);
                sessionDescription.sessionAttributes.addElement(attribute);
            }
        }
    }

    /**
     * Parse time description
     *
     * @return Time description
     */
    private TimeDescription parseTimeDescription() {
        TimeDescription td = new TimeDescription();

        // Time the session is active
        td.timeActive = getLine(bin);

        // Repeat times
        td.repeatTimes = new Vector<>();
        while (getToken(bin, "r=")) {
            String repeatTime = getLine(bin);
            td.repeatTimes.addElement(repeatTime);
        }

        return td;
    }

    /**
     * Parse media descriptions
     */
    private void parseMediaDescriptions() {
        while (getToken(bin, "m=")) {
            Vector<MediaDescription> descs = new Vector<>();

            // Media name and transport address
            String line = getLine(bin);
            int end = line.indexOf(' ');
            String name = line.substring(0, end);

            int start = end + 1;
            end = line.indexOf(' ', start);
            int port = Integer.parseInt(line.substring(start, end));

            start = end + 1;
            end = line.indexOf(' ', start);
            String protocol = line.substring(start, end);

            String payload;
            start = end + 1;
            end = line.indexOf(' ', start);
            while (end != -1) {
                payload = line.substring(start, end);
                descs.addElement(new MediaDescription(name, port, protocol, payload));
                start = end + 1;
                end = line.indexOf(' ', start);
            }
            payload = line.substring(start);
            descs.addElement(new MediaDescription(name, port, protocol, payload));

            // Session and media information
            if (getToken(bin, "i=")) {
                String mediaTitle = getLine(bin);
                for (int i = 0; i < descs.size(); i++) {
                    descs.elementAt(i).mMediaTitle = mediaTitle;
                }
            }

            // Connection information
            if (getToken(bin, "c=")) {
                String connectionInfo = getLine(bin);
                for (int i = 0; i < descs.size(); i++) {
                    descs.elementAt(i).mConnectionInfo = connectionInfo;
                }
            }

            // Bandwidth information
            while (getToken(bin, "b=")) {
                line = getLine(bin);
                int index = line.indexOf(':');
                if (index > 0) {
                    String valueAttribute = line.substring(index + 1);
                    if (line.contains("AS")) {
                        for (int i = 0; i < descs.size(); i++) {
                            descs.elementAt(i).mBandwidthInfo = valueAttribute;
                        }
                    } else if (line.contains("RS")) {
                        for (int i = 0; i < descs.size(); i++) {
                            descs.elementAt(i).mSenderBandwidthInfo = valueAttribute;
                        }
                    } else if (line.contains("RR")) {
                        for (int i = 0; i < descs.size(); i++) {
                            descs.elementAt(i).mReceiverBandwidthInfo = valueAttribute;
                        }
                    }
                }
            }

            // Encryption key
            if (getToken(bin, "k=")) {
                String encryptionKey = getLine(bin);
                for (int i = 0; i < descs.size(); i++) {
                    descs.elementAt(i).mEncryptionKey = encryptionKey;
                }
            }

            // Media attributes
            while (getToken(bin, "a=")) {
                line = getLine(bin);
                int index = line.indexOf(':');
                if (index > 0) {
                    String nameAttribute = line.substring(0, index);
                    String valueAttribute = line.substring(index + 1);
                    MediaAttribute attribute = new MediaAttribute(nameAttribute, valueAttribute);

                    // Dispatch for specific payload
                    if (valueAttribute.indexOf(' ') != -1) {
                        // Add the attribute only for same payload
                        boolean payloadFound = false;
                        for (int i = 0; i < descs.size(); i++) {
                            // Check if first element is a payload
                            if (valueAttribute.startsWith(descs.elementAt(i).mPayload)) {
                                descs.elementAt(i).mMediaAttributes.addElement(attribute);
                                payloadFound = true;
                            }
                        }
                        // Add for all if first element is not a payload
                        if (!payloadFound) {
                            for (int i = 0; i < descs.size(); i++) {
                                descs.elementAt(i).mMediaAttributes.addElement(attribute);
                            }
                        }
                    } else {
                        // Add for all
                        for (int i = 0; i < descs.size(); i++) {
                            descs.elementAt(i).mMediaAttributes.addElement(attribute);
                        }
                    }
                }
            }

            // Copy in media descriptions
            for (int i = 0; i < descs.size(); i++) {
                mediaDescriptions.addElement(descs.elementAt(i));
            }
        }
    }

    /**
     * Returns session attribute
     *
     * @param name Attribute name
     * @return Attribute
     */
    public MediaAttribute getSessionAttribute(String name) {
        if (sessionDescription != null) {
            return sessionDescription.getSessionAttribute(name);
        }
        return null;
    }

    /**
     * Returns a media description
     *
     * @param name Media name
     * @return Media
     */
    public MediaDescription getMediaDescription(String name) {
        MediaDescription description = null;
        if (mediaDescriptions != null) {
            for (int i = 0; i < mediaDescriptions.size(); i++) {
                MediaDescription entry = mediaDescriptions.elementAt(i);
                if (entry.mName.equals(name)) {
                    description = entry;
                    break;
                }
            }
        }
        return description;
    }

    /**
     * Returns media descriptions
     *
     * @param name Media name
     * @return Medias
     */
    public Vector<MediaDescription> getMediaDescriptions(String name) {
        Vector<MediaDescription> result = new Vector<>();
        if (mediaDescriptions != null) {
            for (int i = 0; i < mediaDescriptions.size(); i++) {
                MediaDescription entry = mediaDescriptions.elementAt(i);
                if (entry.mName.equals(name)) {
                    result.add(entry);
                }
            }
        }
        return result;
    }

    /**
     * Returns all media descriptions
     *
     * @return Medias
     */
    public Vector<MediaDescription> getMediaDescriptions() {
        return mediaDescriptions;
    }
}
